﻿using GameSdk.AllMode;
using GameSdk.MsgHelper;
using HPSocket;
using HPSocket.Tcp;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using PinusTools.DataConverter;
using PinusTools.Enums;
using PinusTools.Models;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using PinusClientDemo.AllJobs;

namespace PinusClientDemo.Tools
{
    public class UserHelper
    {
        /// <summary>
        /// 服务器连接状态
        /// </summary>
        public bool ConnectType => tcpPullClient.IsConnected;

        /// <summary>
        /// 消息队列
        /// </summary>
        public Queue<MessageDecodeMod> QueueMsg { get; set; } = new Queue<MessageDecodeMod>();

        /// <summary>
        /// 登录成功后返回的Token
        /// </summary>
        private String LoginToken { get; set; } = String.Empty;

        #region 基础参数初始化 -- 开始

        /// <summary>
        /// 用户会话ID
        /// </summary>
        private String ClientId { get; set; }

        /// <summary>
        /// 心跳检测任务ID
        /// </summary>
        private String HeartBeatJobId { get; set; }

        /// <summary>
        /// 路由事件帮助类
        /// </summary>
        private SdkRouteHelper _routeHelper = new SdkRouteHelper();

        /// <summary>
        /// 服务器连接配置
        /// </summary>
        private ITcpPullClient tcpPullClient = new TcpPullClient
        {
            KeepAliveInterval = 0,
            KeepAliveTime = 0,
            Address = "8.135.60.250",
            Port = 13762
        };


        private String UserName { get; set; }
        private String PassWord { get; set; }

        /// <summary>
        /// 初始化
        /// </summary>
        public UserHelper(string username, string password)
        {
            ClientId = Guid.NewGuid().ToString("N").ToUpper();
            HeartBeatJobId = $@"HeartBeatJob_{ClientId}";

            UserName = username;
            PassWord = password;

            LogHelper.Log.Information($@"初始化用户，账号：{UserName}，密码：{PassWord}");

            tcpPullClient.OnReceive += TcpPullClient_OnReceive;
            tcpPullClient.OnClose += TcpPullClient_OnClose;
            tcpPullClient.OnConnect += TcpPullClient_OnConnect;

            LogHelper.Log.Information($@"连接服务器--开始");
            tcpPullClient.Connect();
        }

        #endregion -- 结束

        #region Socket客户端事件订阅 -- 开始

        private HandleResult TcpPullClient_OnConnect(IClient sender)
        {
            LogHelper.Log.Information($@"发送握手包");
            var sendData = BasicMsgHelper.GetConnectMsg();

            tcpPullClient.Send(sendData, sendData.Length);
            return HandleResult.Ok;
        }

        private HandleResult TcpPullClient_OnClose(IClient sender, SocketOperation socketOperation, int errorCode)
        {
            Task.Run(AutoConnectServer);

            return HandleResult.Ok;
        }

        private HandleResult TcpPullClient_OnReceive(IClient sender, int length)
        {

            tcpPullClient.Peek(4, out var buffer);
            if (buffer == null)
                return HandleResult.Ignore;

            var len = (((int)buffer[1]) << 16 | ((int)buffer[2]) << 8 | (int)buffer[3]) + 4;

            if (length < len)
                return HandleResult.Ignore;

            tcpPullClient.Fetch(len, out var data);

            DataDecodeAndInvoke(data);

            return HandleResult.Ok;
        }

        #endregion Socket客户端事件订阅 -- 结束

        #region 接受消息执行 -- 开始

        /// <summary>
        /// 接受消息解析并且执行
        /// </summary> 
        private void DataDecodeAndInvoke(byte[] data)
        {
            var packageList = Package.Decode(data);

            foreach (PackageDecodeMod decodeMod in packageList)
            {
                switch (decodeMod.Type)
                {
                    case PackageEnum.TYPE_HANDSHAKE:
                        FuncHandshake(decodeMod.Body);
                        break;

                    case PackageEnum.TYPE_HEARTBEAT:
                        break;

                    case PackageEnum.TYPE_DATA:
                        FuncData(decodeMod.Body);
                        break;
                }
            }
        }

        /// <summary>
        /// 服务器握手
        /// </summary>
        /// <param name="receiveData">返回消息对象</param>
        private void FuncHandshake(byte[] receiveData)
        {
            var strJson = Protocol.StrDecode(receiveData);
            var data = JsonConvert.DeserializeObject<HandshakeMod>(strJson);
            if (data == null || data.Code is ResEnum.RES_OLD_CLIENT or not ResEnum.RES_OK)
            {
                tcpPullClient.Stop();
                return;
            }

            //发送握手响应消息
            var sendData = BasicMsgHelper.GetConnectAckMsg();
            tcpPullClient.Send(sendData, sendData.Length);

            this.Login();

            //开启心跳检测任务
            QuartzHelper.AddJob<SendHeartBeatJob>(HeartBeatJobId, "0/15 * * * * ? ", "ITcpPullClient", tcpPullClient);
        }

        /// <summary>
        /// 消息到达事件
        /// </summary> 
        private void FuncData(byte[] receiveData)
        {
            var msg = Message.Decode(receiveData);

            if (msg.Id > 0)
            {
                msg.Route = _routeHelper.GetRoute(msg.Id);

                if (msg.Route == null)
                {
                    _routeHelper.RemoveAction(msg.Id);
                    return;
                }
            }

            QueueMsg.Enqueue(msg);
            LogHelper.Log.Information($@"{msg.Route}|{msg.StrBody}");

            _routeHelper.ExecuteAction(msg.Id, msg.StrBody);
        }
        #endregion 接受消息执行 -- 结束

        #region 用户重新登录方法 -- 开始

        /// <summary>
        /// 自动重连次数
        /// </summary>
        private Int32 AutoLoginCount { get; set; } = 0;

        private async Task AutoConnectServer()
        {
            do
            {
                await Task.Delay(3000);
                AutoLoginCount++;
                LogHelper.Log.Information($@"第{AutoLoginCount}次，尝试重连");
                tcpPullClient.Connect();
            } while (AutoLoginCount <= 5 && !tcpPullClient.IsConnected && !String.IsNullOrWhiteSpace(LoginToken));

            AutoLoginCount = 0;
            LoginToken = String.Empty;

            Thread.Sleep(3000);
            if (!tcpPullClient.IsConnected)
            {
                LogHelper.Log.Information($@"重连失败，尝试重新登录");
                tcpPullClient.Connect();
            }
        }

        #endregion 用户重新登录方法 -- 结束
        
        #region 用户交互方法 -- 开始
        
        #region 登录
        /// <summary>
        /// 用户登录
        /// </summary>
        /// <param name="username">账号</param>
        /// <param name="password">密码</param>
        public void Login(string username = null, string password = null)
        {
            if (String.IsNullOrWhiteSpace(username)) username = UserName;
            if (String.IsNullOrWhiteSpace(password)) password = PassWord;

            if (String.IsNullOrWhiteSpace(username) || String.IsNullOrWhiteSpace(password)) return;
            LogHelper.Log.Information($@"准备登录账号密码");

            var sendData = _routeHelper.Connector_EntryHandler_Entry(username, password, LoginCallback);

            if (!String.IsNullOrWhiteSpace(LoginToken))
            {
                LogHelper.Log.Information($@"Token存在，使用重连方式");
                sendData = _routeHelper.Connector_EntryHandler_Entry(LoginToken);
            }

            tcpPullClient.Send(sendData, sendData.Length);
        }
        /// <summary>
        /// 登录事件回调
        /// </summary>
        /// <param name="data"></param>
        /// <param name="reqId"></param>
        public void LoginCallback(object data, int reqId)
        {
            if (data is String val && !String.IsNullOrWhiteSpace(val))
            {
                var jObj = JsonConvert.DeserializeObject<JToken>(val);
                if (jObj != null && jObj.Value<int>("code") == 200)
                {
                    AutoLoginCount = 0;
                    LoginToken = jObj.Value<string>("data");
                }
                else
                {
                    if (AutoLoginCount > 10) return;

                    Thread.Sleep(5000);
                    AutoLoginCount++;
                    this.Login();
                }
            }
        }
        #endregion
        
        #region 战斗
        /// <summary>
        /// 组队战斗开始
        /// </summary>
        /// <param name="mapid">地图ID</param> 
        public void BattleStart(string mapid)
        {
            var sendData = _routeHelper.LOGIC_TEAMHANDLER_BATTLESTART(mapid);
            tcpPullClient.Send(sendData, sendData.Length);
        }
        #endregion

        #endregion 用户交互方法 -- 结束
    }
}